package base.impl;

import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;

import base.DBUtil;
import base.IBaseDao;
import base.ReflectUtils;

public abstract class BaseDaoImpl<T> implements IBaseDao<T> {

	private String sql ="";
	
	@Override
	public void setSql(String sql) {
		this.sql = sql;
	}
	
	/**
	 * 设计思路：当连接不同数据库时，把父类定义abstract类，可变的部分定义为abstract，让子类去实现
	 * @return
	 */
	public abstract Connection getConnection();
	
	/**
	 * 自动使用注解生成insert语句
	 */
	@Override
	public int add(T entity) throws Exception {
		
		Class<?> clazz = Class.forName(entity.getClass().getName());
		System.out.println(clazz);
		
		//1.通过注解 得到表名
		Annotation[] anno = clazz.getAnnotations();
		
		//@com.zsrt.common.annotation.ScottTable(table=shopping_product_type_t)
		String allfirst = anno[0].toString();
		
		//1.2 解析 allfrist 获取到表名
		String tableName = allfirst.substring(allfirst.lastIndexOf('=')+1, allfirst.length()-1);
		System.out.println(tableName);
		
		//2.获取实体类的所有属性，并转为字段格式字符串
		Field[] declaredFields = clazz.getDeclaredFields();
		//typeName
		String[] fieldStrings = new String[declaredFields.length];
		for(int index = 0 ; index < declaredFields.length ; index++){
			fieldStrings[index] = declaredFields[index].getName().toString();
		}
		
		//2.1 字段格式 字符串  id ==> ID， typeName ==> TYPE_NAME
		//FATHER_ID
		String[] metaDatas = ReflectUtils.getMetasFromFields(fieldStrings);
		
		//2.2把 metaDatas，转为 (ID, TYPE_NAME, FATHER_ID) ---并设置占位符(?,?,?)
		String[] metaAndPla = ReflectUtils.getmetaAndplaceholder(metaDatas);
		
		//3.通过字段，构建出insert语句
		String sql = "insert into "+tableName+"("+metaAndPla[0]+") values "+metaAndPla[1];
		System.out.println(sql);
		
		//4. 操作数据库
		Connection conn = getConnection();
		PreparedStatement ps = conn.prepareStatement(sql);
		//4.1 设置ps中的参数
		int index = 0;
		while(index < fieldStrings.length){
			//------4.1.1 获取到get方法：getReadMethod-----
			PropertyDescriptor pd = new PropertyDescriptor(fieldStrings[index], clazz);
			Method method = pd.getReadMethod();
			
			// 4.1.2 获取到对应属性的类型
			String typeName = declaredFields[index++].getType().getName();
			
			//4.1.3 执行get方法，获得属性值
			String data = method.invoke(entity).toString();
			if (typeName.equals("java.lang.String")) {
				ps.setString(index, data);		
			} else if (typeName.equals("java.lang.Integer")) {
				ps.setInt(index, Integer.parseInt(data));		
			} else if (typeName.equals("java.lang.Long")) {
				ps.setLong(index, Long.parseLong(data));		
			} else if (typeName.equals("java.lang.Float")) {
				ps.setFloat(index, Float.parseFloat(data));		
			} else if (typeName.equals("java.lang.Double")) {
				ps.setDouble(index, Double.parseDouble(data));	
			} else if (typeName.equals("java.lang.Boolean")) {
				ps.setBoolean(index, Boolean.parseBoolean(data));	
			} else {
				return 0;
			}
			System.err.println(data);
		}
		
		int counter = ps.executeUpdate();
		
		//关闭资源
		DBUtil.closeResource(ps, conn);
		return counter;
	}

	/**
	 * 如果不显式设置事务，sql语句只要能成功执行一条，就执行一条
	 * 2.显式提交事务，需要所有sql语句都能成功执行，才会执行
	 */
	@Override
	public int add02(T entity) throws Exception {
		//1.加载驱动，创建连接
		Connection conn = getConnection();
		
		//----conn会自动提交事务：如果设置为false，就不会自动提交(数据库中就没有插入数据)
		conn.setAutoCommit(false);
		
		//2.获取sql语句
//		String sql = "";
		
		//3.构建预编译模块，并填充占位符“?”
		PreparedStatement ps = conn.prepareStatement(sql);
		
		//3.1 获取sql语句中的字段
//		String[] fields = Utils.getInsertFeilds(sql);
		String[] fields=null;
		
		//3.2 通过属性获取到对应的set/get方法
		Field[] declaredFields = entity.getClass().getDeclaredFields();
		
		//3.3 获得get方法,并设置到占位符中
		//3.3.1 遍历declaredFields，获取到sql语句中存在的属性
		int index = 0;
		for(int i = 0 ; i < declaredFields.length; i++){
			Field f = declaredFields[i];
			
			//比较:当在declaredFields中，找到sql语句的一个字段，字段下标index++,并且i重置：i=0
			if(f.getName().equalsIgnoreCase(fields[index])){
				i = 0;
				index++;
				
				//获取到属性的类型
				Class<?> type = f.getType();
				
				//3.3.2 获取到get方法：通过 PropertyDescriptor(属性描述器)；之后index++
				//特别注意：PropertyDescriptor(String propertyName, Class<?> beanClass)
				//			其中的propertyName 必须与属性完全一致（包括大小写）
				PropertyDescriptor pd = new PropertyDescriptor(f.getName(), entity.getClass());
				Method method = pd.getReadMethod();
				
				//3.3.3 执行get方法，获得属性值
				String data = method.invoke(entity).toString();
				
				//3.4 根据类型插入占位符中
				if(type == Integer.class){
					ps.setInt(index, Integer.parseInt(data));
				}else if(type == Double.class || type == Float.class){
					ps.setDouble(index, Double.parseDouble(data));
				}else if(type == String.class){
					ps.setString(index, data);
				}else if(type == Byte.class){
					ps.setBytes(index, data.getBytes());
				}else if(type == Boolean.class){
					ps.setBoolean(index, Boolean.parseBoolean(data));
				}else{
					System.out.println("other type : "+type.getName());
				}
				
				//3.5 直到找到所有的字段，跳出循环
				if(index == fields.length){
					break;
				}
			}
		}
		
		//4.执行sql语句
		int counter = ps.executeUpdate();
		
		//----手动提交事务：使用throws exception，会自动回滚
		conn.commit();
		
		//5.关闭资源
		DBUtil.closeResource(ps, conn);
		return counter;
	}


	@Override
	public int deleteByID(T entity) throws Exception {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public int update(T beforeEntity, T entity) throws Exception {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public T findByID(Integer id) throws Exception {
		//1.加载驱动，创建连接
		Connection conn = DBUtil.getConnectionForMySQL();
		
		//2.获取sql语句
		//3.构建预编译模块，并填充占位符“?”
		PreparedStatement ps = conn.prepareStatement(sql);
		ps.setInt(1, id);
		
		//4.执行sql语句
		ResultSet rs = ps.executeQuery();
		System.out.println(rs.next());
		
		//5.关闭资源
		DBUtil.closeResource(rs,ps, conn);
		return null;
	}

	@SuppressWarnings("unchecked")
	@Override
	public T findByID(String id) throws Exception {
		//00读取xml文件中的package
		//"com.zsrt.myshop.productType.entity.ProductTypeEntity"
		String entityPath = ReflectUtils.getEntityPath();
		
		Class<?> clazz = Class.forName(entityPath);
		Object obj = clazz.newInstance();
		System.out.println("clazz = "+clazz);
		
		//1.通过注解 得到表名
		Annotation[] anno = clazz.getAnnotations();
		
		//@com.zsrt.common.annotation.ScottTable(table=shopping_product_type_t)
		String allfirst = anno[0].toString();
		
		//1.2 解析 allfrist 获取到表名
		String tableName = allfirst.substring(allfirst.lastIndexOf('=')+1, allfirst.length()-1);
		System.out.println("tableName = "+tableName);
		
		//2.获取实体类的所有属性，并转为字段格式字符串
		Field[] declaredFields = clazz.getDeclaredFields();
		//typeName
		String[] fieldStrings = new String[declaredFields.length];
		for(int index = 0 ; index < declaredFields.length ; index++){
			fieldStrings[index] = declaredFields[index].getName().toString();
		}
		
		//2.1 字段格式 字符串  id ==> ID， typeName ==> TYPE_NAME
		//FATHER_ID
		String[] metaDatas = ReflectUtils.getMetasFromFields(fieldStrings);
		
		//2.2把 metaDatas，转为 (ID, TYPE_NAME, FATHER_ID) ---并设置占位符(?,?,?)
		String[] metaAndPla = ReflectUtils.getmetaAndplaceholder(metaDatas);
		
		//3.通过字段，构建出insert语句
		String sql = "select "+metaAndPla[0]+" from "+tableName+" where "+metaDatas[0]+" = "+id;
		System.err.println("sql = "+sql);
		
		//4. 操作数据库
		Connection conn = getConnection();
		PreparedStatement ps = conn.prepareStatement(sql);
		ResultSet rs = ps.executeQuery();
		ResultSetMetaData metaData = rs.getMetaData();
		
		//5.接收rs中的数据
		while(rs.next()){
			int index = 0;
			for(int metaIndex = 1 ; metaIndex <= metaData.getColumnCount() ;metaIndex++){
				//---5.1通过set方法把值设到 clazz中：getReadMethod-----
				PropertyDescriptor pd = new PropertyDescriptor(fieldStrings[index], clazz);
				Method method = pd.getWriteMethod();
				
				// 5.2 获取到对应属性的类型
				String typeName = declaredFields[index++].getType().getName();
				
				String metaName = metaData.getColumnName(metaIndex);
				System.err.println(metaName);
				// 5.3 执行set方法
				if (typeName.equals("java.lang.String")) {
					method.invoke(obj,rs.getString(metaName));		
				} else if (typeName.equals("java.lang.Integer")) {
					method.invoke(obj,rs.getInt(metaName));		
				} else if (typeName.equals("java.lang.Long")) {
					method.invoke(obj,rs.getLong(metaName));
				} else if (typeName.equals("java.lang.Float")) {
					method.invoke(obj,rs.getFloat(metaName));
				} else if (typeName.equals("java.lang.Double")) {
					method.invoke(obj,rs.getDouble(metaName));
				} else if (typeName.equals("java.lang.Boolean")) {
					method.invoke(obj,rs.getBoolean(metaName));
				} else {
					return null;
				}
			}
		}
		
		//关闭资源
		DBUtil.closeResource(rs,ps, conn);
		
		T newInstance = (T) obj;
		System.out.println("newInstance = "+newInstance);
		return newInstance;
	}

}
